package miniworld;

import homeMadeExceptions.Routing_HostNotAvailableException;
import homeMadeExceptions.Routing_HostNotReachableException;

import java.util.HashMap;

import ui.observ.PhysicalNetworkObservableUnit;
import datatypes.BluetoothDeviceAddress;
import datatypes.Edge;
import datatypes.Host;

public class PhysicalNetwork {
	//Variables
	private HashMap<String,Edge> knownEdges = null;
	private HashMap<String,Host> knownPeers = null;
	
	//id of the Simulation engine
	private int simID = -1;
	
	public PhysicalNetwork (int SimulationEngineID){
		this.simID = SimulationEngineID;
	}
	
	// API to Routing Protocol for Building Network Structure
	// == Network Construction API
	protected void addPeer (Host host){
		if (knownPeers == null){
			knownPeers = new HashMap<String,Host>();
		}
		knownPeers.put(host.getBd_Addr().getAddress(), host);
	}
	
	protected void removePeer (Host host){		
		if (knownPeers != null){
			knownPeers.remove(host.getBd_Addr().getAddress());
		}
	}

	protected void removeEdge (String edge){
		if (knownEdges != null){
			if (knownEdges.get(edge) != null){
				PhysicalNetworkObservableUnit.getInstance(simID).edgeRemoved(knownEdges.get(edge));
				knownEdges.remove(edge);
			}
		}
	}

	protected void sendMessage (BluetoothDeviceAddress from, BluetoothDeviceAddress to) throws Routing_HostNotReachableException,Routing_HostNotAvailableException  {
		if (knownEdges == null || knownPeers == null){
			throw new Routing_HostNotReachableException();
		}
		if (!isHostAvailable(from)){
			throw new Routing_HostNotReachableException();
		}
		if (!isHostAvailable(to)){
			throw new Routing_HostNotAvailableException();
		}
		String EdgeId_Version1 = "H" + this.getHostForBD_Addr(from).getID() + "H" + this.getHostForBD_Addr(to).getID() + "W5";	
		String EdgeId_Version2 = "H" +  this.getHostForBD_Addr(to).getID() + "H" +this.getHostForBD_Addr(from).getID()  + "W5";
		if (!isEdgeAvailable(EdgeId_Version1) && !isEdgeAvailable(EdgeId_Version2)){
			throw new Routing_HostNotReachableException();
		}
		
	}

	protected void addEdge (Host host1, Host host2, int weight){
		String EdgeId = "H" + host1.getID() + "H" + host2.getID() + "W" + weight;
		if (knownEdges == null){
			knownEdges = new HashMap<String,Edge>();
		}
		knownEdges.put(EdgeId, new Edge(host1, host2, EdgeId, weight));
		PhysicalNetworkObservableUnit.getInstance(simID).edgeAdded(knownEdges.get(EdgeId));
	}
	

	//API vor sending Physical Messages
	//Network USING API
	private void sendMessage (Edge edge) throws Routing_HostNotReachableException,Routing_HostNotAvailableException  {
		if (knownEdges == null || knownPeers == null){
			throw new Routing_HostNotReachableException();
		}
		if (!isEdgeAvailable(edge)){
			throw new Routing_HostNotReachableException();
		}
		if (!isHostAvailable(edge.getSecondHost())){
			throw new Routing_HostNotReachableException();
		}
		if (!isHostAvailable(edge.getFirstHost())){
			throw new Routing_HostNotAvailableException();
		}
	}	
	
	private void sendMessage (Host from, Host to) throws Routing_HostNotReachableException,Routing_HostNotAvailableException  {
		String EdgeId = "H" + from.getID() + "H" + to.getID() + "W5";	
		if (knownEdges == null || knownPeers == null){
			throw new Routing_HostNotReachableException();
		}
		if (!isEdgeAvailable(EdgeId)){
			throw new Routing_HostNotReachableException();
		}
		if (!isHostAvailable(from)){
			throw new Routing_HostNotReachableException();
		}
		if (!isHostAvailable(to)){
			throw new Routing_HostNotAvailableException();
		}
	}	
	
	//Private Methods 
	private boolean isEdgeAvailable (Edge edge){
			if (knownEdges != null){
				return knownEdges.get(edge.getEdgeID()) == null;
			}
			return false;
	}

	private boolean isEdgeAvailable (String edge){
	if (knownEdges != null){
		return knownEdges.get(edge) != null;
	}
	return false;
	}

	private boolean isHostAvailable(Host host) {
		if (knownPeers != null) {
			return (knownPeers.get(host.getBd_Addr().getAddress()) != null);
		}
		return false;
	}

	private boolean isHostAvailable(BluetoothDeviceAddress host) {
		if (knownPeers != null) {
			return (knownPeers.get(host.getAddress()) != null);
		}
		return false;
	}
	
	private Host getHostForBD_Addr(BluetoothDeviceAddress host) {
		if (knownPeers != null) {
			return knownPeers.get(host.getAddress()) ;
		}
		return null;
	}
	
	private HashMap<String, Edge> getEdges () {
		return this.knownEdges;
	}
	
}
